<script setup>

import {nextTick, onMounted, ref} from "vue";
import MessageRow from "./components/MessageRow.vue"
import SessionItem from "./components/SessionItem.vue";
import MessageInput from "./components/MessageInput.vue"
import {CirclePlus, Close, EditPen} from "@element-plus/icons-vue";
import {
  queryChatSession,
  saveOrUpdateChatSession,
} from "./api/chat-session.js";
import {createSse, queryChatMessageList} from "./api/chat-message.js";
import dayjs from "dayjs";
import {ElMessage} from "element-plus";

// 观察者监听“加载更多”按钮
const observer = new IntersectionObserver((entries) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting && entry.target.id === 'loading') {
      loadData();
    }
  })
})
const isEdit = ref(false)
// 当前的会话
const activeSession = ref({messages: []})
// 用户的所有会话
const sessionList = ref([])
const isDisabled = ref(false)

onMounted(async () => {
  // 查询当前用户的聊天会话
  const res = await queryChatSession({userId: '67514114a8b8693f3c11c78b'})
  // 将会话添加到列表中
  sessionList.value.push(...res.data);
  // 默认选中第一个聊天会话
  if (sessionList.value.length > 0) {
    activeSession.value = sessionList.value[0];
  }
  // 滑动到底部
  scrollToBottom();
  // 观察 “加载更多”
  const loading = document.getElementById("loading");
  observer.observe(loading);
});

const loadData = async () => {
  // 获取当前页码
  const pageNum = Math.ceil(activeSession.value.messages.length / 10);
  // 获取当前页码的数据
  const res = await queryChatMessageList({
    sessionId: activeSession.value.id,
    page: pageNum,
    size: 10,
  });
  // 已经是最新的数据了
  if (res.data.length === 0) {
    const loading = document.getElementById("loading");
    observer.unobserve(loading);
    loading.remove();
  } else {
    // 将数据添加到当前会话中
    activeSession.value.messages.push(...res.data);
  }
}

// 切换会话
const handleSessionSwitch = (session) => {
  activeSession.value = session;
}

// 从会话列表中删除会话
const handleDeleteSession = (session) => {
  const index = sessionList.value.findIndex((value) => {
    return value.id === session.id;
  });
  sessionList.value.splice(index, 1);
  ElMessage.success("删除成功！")
}
// 新增会话
const handleCreateSession = async () => {
  const res = await saveOrUpdateChatSession({topic: '新的聊天', creatAt: {id: '67514114a8b8693f3c11c78b'}});
  sessionList.value.unshift(res.data);
  activeSession.value = res.data;
  activeSession.value.messages = []
}

const handleUpdateSession = async () => {
  await saveOrUpdateChatSession(activeSession.value);
  isEdit.value = false;
  ElMessage.success("更新成功！")
}

const responseMessage = ref({});
// 接收ChatGpt回复
const receiveMessage = () => {
  const eventSource = new EventSource(`http://localhost:8080/chat/message?sessionId=${activeSession.value.id}`,);

  eventSource.onmessage = function (event) {
    responseMessage.value.content += event.data;
    // 滑动到底部
    scrollToBottom();
  };
  eventSource.onclose = function () {
    isDisabled.value = false;
    // 滑动到底部
    scrollToBottom();
  }
  eventSource.onerror = function (e) {
    eventSource.close();
    isDisabled.value = false;
  };
}

// 发送消息
const handleSendMessage = async (content) => {
  // 消息为空时 提示消息不能为空
  if (content.trim() === "") {
    ElMessage.error("消息不能为空!")
    return;
  }
  if (isDisabled.value === true) {
    ElMessage.error("请等待回复完成！")
    return;
  }
  isDisabled.value = true;
  // 新建一个ChatGPT回复对象，不能重复使用同一个对象。
  responseMessage.value = {
    role: "assistant",
    content: "",
    // 因为回复的消息没有id，所以统一将创建时间+index当作key
    createTime: dayjs().format("YYYY-MM-DD HH:mm:ss"),
  };
  // 用户的提问
  const chatMessage = {
    session: {id: activeSession.value.id},
    content: content,
    role: "user",
    createTime: dayjs().format("YYYY-MM-DD HH:mm:ss"),
  }
  // 创建sse连接
  await createSse(chatMessage);
  // 接收回复消息
  receiveMessage();
  // 将两条消息显示在页面中
  activeSession.value.messages.push(...[chatMessage, responseMessage.value]);
  // 滑动到底部
  scrollToBottom();
}

// 确保消息列表滚动到底部
const scrollToBottom = () => {
  nextTick(() => {
    const messageList = document.getElementById('messageList')
    if (messageList) {
      messageList.scrollTop = messageList.scrollHeight;
    }
  })
};

</script>

<template>
  <!--最外层页面-->
  <div class="home-view">
    <!--聊天面板-->
    <div class="chat-panel">
      <!--左侧的会话列表-->
      <div class="session-panel">
        <div class="title">ChatGPT助手</div>
        <div class="description">构建你的AI助手</div>
        <!--会话列表-->
        <div class="session-list">
          <session-item v-for="(session,index) in sessionList"
                        :key="session.id"
                        :active="session.id === activeSession.id"
                        :session="session"
                        class="session"
                        @click="handleSessionSwitch(session)"
                        @delete="handleDeleteSession(session)"></session-item>
        </div>
        <div class="button-wrapper">
          <div class="new-session">
            <el-button @click="handleCreateSession">
              <el-icon :size="15" class="el-icon--left">
                <CirclePlus></CirclePlus>
              </el-icon>
              新的聊天
            </el-button>
          </div>
        </div>
      </div>
      <!--右侧的消息记录-->
      <div class="message-panel">
        <!--会话名称-->
        <div class="header">
          <div class="front">
            <!--如果处于编辑状态则显示输入框让用户修改-->
            <div v-if="isEdit" class="title">
              <!-- 按回车代表确认修改 -->
              <el-input v-model="activeSession.topic"
                        @keydown.enter="handleUpdateSession"></el-input>
            </div>
            <!--否则正常显示标题-->
            <div v-else class="title">{{ activeSession.topic }}</div>
            <div class="description">
              与ChatGPT的{{ activeSession.messages ? activeSession.messages.length : 0 }}条对话
            </div>
          </div>
          <!--尾部的编辑按钮-->
          <div class="rear">
            <el-icon :size="20">
              <!-- 不处于编辑状态显示编辑按钮 -->
              <EditPen @click="isEdit = true" v-if="!isEdit"></EditPen>
              <!-- 处于编辑状态显示取消编辑按钮 -->
              <Close @click="isEdit = false" v-else></Close>
            </el-icon>
          </div>
        </div>
        <el-divider :border-style="'solid'"></el-divider>
        <div class="message-list" id="messageList">
          <!-- 动态列表进行过渡动画 -->
          <transition-group name="list">
            <MessageRow
                v-for="(message,index) in activeSession.messages"
                :key="index"
                :avatar="'https://i.postimg.cc/g04KGGqx/OIP.jpg'"
                :message="message"
            ></MessageRow>
          </transition-group>
          <div id="loading">加载中...</div>
        </div>
        <!-- 监听发送事件 -->
        <message-input :is-disabled="isDisabled" class="message-input" @send="handleSendMessage"></message-input>
      </div>
    </div>
  </div>
</template>

<style scoped>
.home-view {
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
}

.chat-panel {
  display: flex;
  width: 90%;
  border-radius: 20px;
  background-color: white;
  box-shadow: 0 0 20px 20px rgba(0, 0, 0, 0.05);
}

.session-panel {
  width: 25%;
  border-top-left-radius: 20px;
  border-bottom-left-radius: 20px;
  padding: 20px;
  position: relative;
  border-right: 1px solid rgba(0, 0, 0, 0.07);
  background-color: rgb(231, 248, 255);
}

.message-panel {
  width: 75%;
  display: flex;
  flex-direction: column;
  height: calc(100vh - 64px);
}

.title {
  margin-top: 20px;
  font-size: 20px;
  text-align: center;
}

.description {
  color: rgba(0, 0, 0, 0.7);
  font-size: 10px;
  margin-top: 10px;
  text-align: center;
}

.button-wrapper {
  /* session-panel是相对布局，这边的button-wrapper是相对它绝对布局 */
  position: absolute;
  bottom: 20px;
  left: 0;
  display: flex;
  /* 让内部的按钮显示在右侧 */
  justify-content: flex-end;
  width: 100%;
}

.new-session {
  margin-right: 20px;
}

.session{
  margin-top: 5px;
}

.header {
  padding: 20px 20px 0 20px;
  display: flex;
  /* 会话名称和编辑按钮在水平方向上分布左右两边 */
  justify-content: space-between;
}

.front .title {
  color: rgba(0, 0, 0, 0.7);
  font-size: 20px;
}

.front .description {
  margin-top: 10px;
  color: rgba(0, 0, 0, 0.5);
}

/* 尾部的编辑和取消编辑按钮 */
.rear {
  display: flex;
  align-items: center;
}

.message-list {
  flex: 1;
  padding: 15px;
  /*  消息条数太多时，溢出部分滚动*/
  overflow-y: scroll;
}

.message-list > .list-enter-active, .list-leave-active {
  transition: all 0.5s ease;
}

.message-list > .list-enter-from, .list-leave-to {
  opacity: 0;
  transform: translateX(30px);
}

.message-input {
  margin-top: auto;
}

</style>
